Cpp程序的编译过程

前言

首先gcc与GCC要区分对待,GCC原名为GNU C Compiler,是一个C编译器的代号,但是后来不断地扩展,开始支持很多语言,GCC也就变成了编译器家族GNU Compiler Collection.
另外GNU的意思是GNU not Unix的递归简写(-_-所以那个G到底是啥意思?),这是一个由Stallman发起的一个操作系统计划,但是最后各种编译器什么的工具都写好了,核心的内核没有开发成功,但最后Linux填补了这个空白.

编译的四个阶段

如果只是在命令行编译一下c或c++程序,直接看后面的命令就可以了,但是了解一下编译的的过程,会加深对gcc的理解.
gccfourstep.jpg
(环境:ubuntu 18.04 | 已安装gcc/g++)
1.预处理阶段 .c >>> .i
在这个阶段,编译器先把人方便看的程序处理成编译器方便看的程序.

  • #define删掉,然后展开所有宏定义
  • 处理那些#include,把包含的那些头文件复制过来.
  • 删掉注释

  • 下面这个test.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream> 
    #include <cstdio>
    #define AA a*a

    using namespace std;

    int main(){
    int a=2;
    int A=AA;//这里会用宏定义替换掉啊
    cout<<A<<endl;
    cout<<"hello world!"<<endl;
    return 0;
    }

用命令 g++ -E test.cpp -o test.i 处理一下生成tes.i文件
(不加-o,就都会输出到屏幕上,不生成.i文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
此处省略两万行...
static ios_base::Init __ioinit;
}
\# 2 "test.cpp" 2
\# 1 "/usr/include/c++/7/cstdio" 1 3
\# 39 "/usr/include/c++/7/cstdio" 3
\# 40 "/usr/include/c++/7/cstdio" 3
\# 3 "test.cpp" 2
\# 5 "test.cpp" //可以看到define,include
using namespace std; // 什么的都没了,被替换为了上面两万多行

int main(){
int a=2;
int A=a*a;//此处宏定义被替换了,注释也没了
cout<<A<<endl;
cout<<"hello world!"<<endl;
return 0;
}

2.编译为汇编代码 .i >>> .s
这一阶段,编译器进行:

  • 语法分析
  • 词法分析
  • 生成汇编代码
    通过命令 g++ -S test.i -o test.s 可以生成汇编代码,代码太长,就不粘贴了.

3.生成机器码 .s >>> .o
通过 g++ -c test.s -o test.o 生成目标文件,如果程序只有一个文件,这时候应该就可以执行了.如果有多个.o文件,还得需要下一步链接后再运行.
这一步也可以用GNU自带的汇编器as来将汇编文件生成机器码,命令如下:

1
as test.s -o test.o

这里可以借助hexdump工具来查看二进制文件

1
2
hexdump -C test.o > a.txt
vim a.txt

4.链接.o文件 .o >>> 可执行文件

1
2
g++ test.o -o test
./test #然后就可以运行文件了

gcc & g++

这两个都是编译器的名字,一般看名字会感觉gcc用来编译c语言,g++用来编译c++的,但实际上这两个既能编译c语言,又能编译c++,g++可以算是gcc的另一个版本.
当gcc编译c程序时 gcc a.c -o a #这样就生成可执行文件a了
当gcc编译c++程序时 gcc a.cpp -lstdc++ #加个-lstdc++链接上c++的库才行.
当g++编译c程序时,跟gcc用法是一样的,实际上g++在编译c程序是也是直接调用的gcc
当g++编译cpp程序时,看下面一节.

最最常用的命令

无论编译.c还是.cpp,g++都挺方便,一般直接都用g++就可以了.
有时候只是想快速的运行一个c或cpp程序而已,就不用看上面那一坨了…直接用这条命令就够了:

1
2
3
4
5
g++ test.cpp -o test  #cpp程序
./test #运行
------------------
g++ test.c op test #c程序
./test #

其他命令

1
2
3
g++ -c main.c #生成.o文件(.object)
g++ -c main.c -o main.o #生成指定名字的.o文件
g++ main.o #将main.o文件生成a.out文件(.obj >> .out)

常用选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-E	只运行 C 预编译器。
-S 生成汇编代码
-c 只编译并生成目标文件。
-g 生成调试信息。GNU 调试器可利用该信息。
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY 连接时搜索指定的函数库LIBRARY。
-o FILE 生成指定的输出文件。用在生成可执行文件时。
-O0 不进行优化处理。
-O 或 -O1 优化生成代码。
-O2 进一步优化。
-O3 比 -O2 更进一步优化,包括 inline 函数。
-w 不生成任何警告信息。
-Wall 生成所有警告信息。

参考

C语言中文网
GCC编译c语言程序完整演示
c语言真正的编译过程
Linux查看二进制文件内容
GCC

欢迎与我分享你的看法。
转载请注明出处:http://taowusheng.cn/